package cn.lovezaoan.hpcenter.parse
{
    import flash.events.Event;
    import flash.html.HTMLLoader;
    import flash.net.URLRequest;
    import flash.utils.Timer;
    
    import mx.collections.ArrayList;
    
    /**
     * html内容提取类。<br>
     * 根据html地址和提取规则从页面中提取符合规则的元素。<br>
     * 提取规则类似css的selector。<br>
     * 如果需要多个规则来描述要提取的元素时，每个规则之间用分隔符EXTRACT_RULE_SPLITER隔开。<br>
     * 规则有三个如下，分别用前缀“.”和“#”区分类名和id名，没有前缀的就是tag：<br>
     * 1、描述类名：.className；<br>
     * 2、描述id：#idName;<br>
     * 3、描述tag：tagName[attName="attValue"]
     *
     * @author Administrator
     *
     */
    public class HtmlExtract
    {
        /**
         * 提取规则字符串中分割多个规则的分隔符
         */
        public static var EXTRACT_RULE_SPLITER:String = ",";
        
        public function HtmlExtract(url2Extract:String)
        {
            this.url2Extract = url2Extract;
            this.waitingTime = 30000; // 默认等待时间为半分钟
            
            var urlQequest:URLRequest = new URLRequest(this.url2Extract);
            _html = new HTMLLoader();
            _html.load(urlQequest);
            _html.addEventListener(Event.COMPLETE, htmlLoadSuccessful);
        }
        
        private var _html:HTMLLoader;
        
        private var document:Object;
        
        private var timer:Timer;
        
        private var url2Extract:String;
        
        private var waitingTime:int;
        
        /**
         * 基于提取规则，提取需要的html内容。<br>
         * 调用本方法的前提是_html已经装载完毕，所以要先检查是否装载成功。<br>
         * 如果超时仍未装载成功，否则抛出异常。
         * @param rule
         * @return
         *
         */
        public function extract(rule:String):ArrayList
        {
            checkAndWaitHtmlLoad();
            
            var resList:ArrayList = new ArrayList();
            var rules:Array = rule.split(EXTRACT_RULE_SPLITER);
            var indexOfRules:int = 0;
            var singleRule:String;
            var element2Parse:ArrayList = new ArrayList(); // 待解析element
            var oldElement2Parse:ArrayList; // old的待解析element
            var tmpElement2Parse:Object; // 包含待解析element的__HTMLScriptObject对象
            
            // 以document作为初始的待解析element开始解析过程
            element2Parse.addItem(this.document);
            while (hasElementAndRule2Parse(indexOfRules, rules.length, element2Parse))
            {
                singleRule = rules[indexOfRules++];
                oldElement2Parse = element2Parse;
                element2Parse = new ArrayList();
                for (var j:int = 0; j < oldElement2Parse.length; j++)
                {
                    tmpElement2Parse = this.extractFromSingleHtmlElement(oldElement2Parse.getItemAt(j), singleRule);
                    if (tmpElement2Parse == null || tmpElement2Parse.length == 0)
                    {
                        break;
                    }
                    element2Parse = createNewElement2Parse(element2Parse as ArrayList, tmpElement2Parse);
                }
            }
            
            return element2Parse;
        }
        
        /**
         * 基于一组规则提取对应的一组html元素。<br>
         * 基于一个规则提取对应的html元素，请参见function extract(rule:String)。
         * @param rules
         * @return
         *
         */
        public function extractMultiple(rules:ArrayList):ArrayList
        {
            var res:ArrayList = new ArrayList();
            if (rules != null && rules.length > 0)
            {
                for (var i:int = 0; i < rules.length; i++)
                {
                    res.addItem(this.extract(rules.getItemAt(i) as String));
                }
            }
            
            return res;
        }
        
        public function extractMultipleArray(rules:Array):ArrayList
        {
            var res:ArrayList = new ArrayList();
            if (rules != null && rules.length > 0)
            {
                for (var i:int = 0; i < rules.length; i++)
                {
                    res.addItem(this.extract(rules[i] as String));
                }
            }
            
            return res;
        }
        
        public function get html():HTMLLoader
        {
            return _html;
        }
        
        private function checkAndWaitHtmlLoad():void
        {
            if (document == null)
            {
                timer = new Timer(this.waitingTime, 1);
                timer.start();
                while (document == null && timer.running)
                {
                    // 不做任何事情，只是等待this.waitingTime的时间看_html是否装载完毕（document非空）
                    trace("wating for html loading");
                }
                
                if (!timer.running)
                {
                    throw new Error("页面装载超时(" + (this.waitingTime / 1000) + "s)！");
                }
                if (timer)
                {
                    timer.stop();
                }
                timer = null;
            }
        }
        
        private function createNewElement2Parse(element2Parse:ArrayList, element2ParseTmp:Object):ArrayList
        {
            if (isSingleHTMLScriptObject(element2ParseTmp))
            {
                element2Parse.addItem(element2ParseTmp);
            }
            else
            {
                for (var i:int = 0; i < element2ParseTmp.length; i++)
                {
                    (element2Parse as ArrayList).addItem(element2ParseTmp[i]);
                }
            }
            
            return element2Parse;
        }
        
        private function extractFromSingleHtmlElement(element:Object, rule:String):Object
        {
            var res:Object = null;
            
            if (rule.indexOf(".") != -1)
            {
                // class: .header
                res = element.getElementsByClassName(rule.substring(1));
            }
            else if (rule.indexOf("#") != -1)
            {
                // id: #myDiv
                res = element.getElementById(rule.substring(1));
            }
            else
            {
                // tag name: a[src="logo.gif"]
                var tag:String = rule;
                var att:String = null;
                var value:String = null;
                if (rule.indexOf("[") != -1)
                {
                    tag = rule.substring(0, rule.indexOf("["));
                    var tmpStr:String = rule.substring(rule.indexOf("[") + 1, rule.length - 1);
                    att = tmpStr.substring(0, tmpStr.indexOf("="));
                    value = tmpStr.substring(tmpStr.indexOf("\"") + 1, tmpStr.length - 1);
                }
                res = listElements(element, tag, att, value);
            }
            
            return res;
        }
        
        private function hasElementAndRule2Parse(indexOfRules:int, lengthOfRules:int,
                                                 element2Parse:ArrayList):Boolean
        {
            return indexOfRules < lengthOfRules && element2Parse != null && element2Parse.length > 0;
        }
        
        
        
        private function htmlLoadSuccessful(event:Event):void
        {
            // 当_html装载完成后获取的document才是有效的
            document = _html.window.document;
            if (timer)
            {
                timer.stop();
            }
            timer = null;
        }
        
        private function isSingleHTMLScriptObject(element:Object):Boolean
        {
            var res:Boolean = true;
            
            // 作为从html页面解析得到的element是flash.html.__HTMLScriptObject对象，但是有两种形式：
            // 一是一个纯粹的flash.html.__HTMLScriptObject对象（相对于第二种形式）；
            // 二是有index个__HTMLScriptObject对象作为其属性，且还有属性item和length(=index)的__HTMLScriptObject对象。
            // 其中这多个__HTMLScriptObject可由element2ParseTmp[i]获取。
            // 因为关于__HTMLScriptObject的信息不多，所以采用try-catch方式，如果element不抛出异常，则可判定为是第二种情况。否则是的一种情况。
            try
            {
                if (element != null && element.length > 0)
                {
                    res = false;
                }
            }
            catch (err:Error)
            {
                
            }
            
            return res;
        }
        
        private function listElements(element:Object, whichTag:String, whichAttr:String,
                                      whichValue:String):Object
        {
            var startElement;
            var listElement_arr = new Array();
            
            if (whichTag)
            {
                startElement = element.getElementsByTagName(whichTag);
            }
            else
            {
                startElement = (element.all) ? element.all : element.getElementsByTagName("*");
            }
            if (whichAttr)
            {
                for (var i:int = 0; i < startElement.length; i++)
                {
                    if (startElement[i].getAttribute(whichAttr))
                    {
                        if (whichValue)
                        {
                            if (startElement[i].getAttribute(whichAttr) == whichValue)
                            {
                                listElement_arr[listElement_arr.length] = startElement[i];
                            }
                        }
                        else
                        {
                            listElement_arr[listElement_arr.length] = startElement[i];
                        }
                    }
                }
                
            }
            else
            {
                listElement_arr = startElement;
            }
            return listElement_arr;
        }
    }
}